# GPS 分段里程重复插入问题修复说明 ## 问题描述 在执行 GPS 分段里程计算时,系统抛出以下异常: ``` org.springframework.jdbc.UncategorizedSQLException: ### Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '9-2026-03-15 06:10:00' for key 'tb_vehicle_gps_segment_mileage.uk_vehicle_time' ``` ## 根本原因 数据库表 `tb_vehicle_gps_segment_mileage` 定义了唯一索引 `uk_vehicle_time (vehicle_id, segment_start_time)`,用于确保同一车辆在同一时间段的分段里程记录只有一条。 虽然在代码层面(`VehicleGpsSegmentMileageServiceImpl.java` 第 390 行)已经有检查逻辑: ```java if (isSegmentAlreadyCalculated(vehicleId, segmentStartTime, segmentGpsList)) { previousSegmentLastPoint = segmentGpsList.get(segmentGpsList.size() - 1); continue; } ``` 但在**并发场景**下,仍可能出现竞态条件(Race Condition): 1. **线程 A** 检查时间段 2026-03-15 06:10:00,发现不存在 2. **线程 B** 同时检查同一时间段,也发现不存在 3. **线程 A** 执行 INSERT 4. **线程 B** 尝试执行 INSERT,违反唯一索引约束,抛出异常 ## 解决方案 修改 MyBatis Mapper XML 文件中的 INSERT 语句,使用 MySQL 的 `ON DUPLICATE KEY UPDATE` 语法: ### 修改文件 - `ruoyi-system/src/main/resources/mapper/system/VehicleGpsSegmentMileageMapper.xml` ### 修改内容 在原有的 `insertVehicleGpsSegmentMileage` 方法中添加 `ON DUPLICATE KEY UPDATE` 子句: ```xml INSERT INTO tb_vehicle_gps_segment_mileage ON DUPLICATE KEY UPDATE vehicle_no = VALUES(vehicle_no), segment_end_time = VALUES(segment_end_time), start_longitude = VALUES(start_longitude), start_latitude = VALUES(start_latitude), end_longitude = VALUES(end_longitude), end_latitude = VALUES(end_latitude), segment_distance = VALUES(segment_distance), gps_point_count = VALUES(gps_point_count), gps_ids = VALUES(gps_ids), task_id = VALUES(task_id), task_code = VALUES(task_code), calculate_method = VALUES(calculate_method), update_time = NOW() ``` ## 技术优势 使用 `ON DUPLICATE KEY UPDATE` 的好处: 1. **原子性操作**:数据库层面保证插入或更新的原子性,避免并发冲突 2. **无需额外查询**:不需要先 SELECT 再决定 INSERT 还是 UPDATE 3. **性能更优**:减少一次数据库查询开销 4. **代码简洁**:不需要复杂的异常捕获和重试逻辑 5. **数据一致性**:如果发生重复,自动更新已有记录而不是报错 ## 影响范围 - **受影响的功能**:GPS 分段里程计算 - **受影响的表**:`tb_vehicle_gps_segment_mileage` - **受影响的接口**:所有调用 `insertVehicleGpsSegmentMileage` 的方法 ## 测试建议 1. **并发测试**:模拟多个线程同时计算同一车辆的 GPS 分段里程 2. **重复数据测试**:手动构造重复的车辆 ID + 时间段开始时间,验证是否能正确更新 3. **回归测试**:确保正常的插入功能不受影响 ## 部署步骤 1. 重启应用服务器即可生效 2. 无需执行任何 SQL 脚本 3. 无需修改数据库表结构 ## 后续优化建议 1. **监控日志**:观察是否还有其他并发场景导致的类似问题 2. **事务优化**:对于批处理操作,考虑添加适当的事务隔离级别 3. **锁机制**:如果问题仍然存在,可以考虑使用数据库行锁或分布式锁 ## 相关文件 - `ruoyi-system/src/main/resources/mapper/system/VehicleGpsSegmentMileageMapper.xml` - `ruoyi-system/src/main/java/com/ruoyi/system/service/impl/VehicleGpsSegmentMileageServiceImpl.java` - `ruoyi-system/src/main/java/com/ruoyi/system/domain/VehicleGpsSegmentMileage.java` - `sql/vehicle_gps_segment_mileage.sql` --- **修复日期**: 2026-03-16 **修复人员**: AI Assistant **问题类型**: 并发数据一致性